package com.prestamomo.vil.ppa.pmvilnet.pmvilutils;

import androidx.annotation.NonNull;

import java.nio.charset.Charset;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;

public abstract class PmvilAnalCode {
    static class PmvilAnCot {
        int codePmvilWorkArea;
        long codePmviltWorkArea;
        byte[] codePmvilBuffer;

        int codePmvilCpos, codePmvilReadPos;

        boolean codePmivlceof;

        int codecPmvilCurrentLinePos, codecPmvilModulus;

        PmvilAnCot() {
        }
        public String toString() {
            return String.format("%s[buffer=%s, currentLinePos=%s, eof=%s, ibitWorkArea=%s, lbitWorkArea=%s, " +
                            "modulus=%s, pos=%s, readPos=%s]", this.getClass().getSimpleName(), Arrays.toString(codePmvilBuffer),
                    codecPmvilCurrentLinePos, codePmivlceof, codePmvilWorkArea, codePmviltWorkArea, codecPmvilModulus, codePmvilCpos, codePmvilReadPos);
        }
    }

    static final int codecPmvilEOF = -1;

    public static final int codecPmvilMIME_CHUNK_SIZE = 76;
    protected static final int CodecPmvilMASK_8BITS = 0xff;

    protected static final byte CodecPmvilPAD_DEFAULT = '=';

    @Deprecated
    protected final byte CodecPmvilPAD = CodecPmvilPAD_DEFAULT;

    protected final byte codePmvilcpad;

    private final int codePmvilcunencodedBlockSize;
    private final int codecPmvilencodedBlockSize;

    protected final int codecPmvillineLength;

    private final int codecPmvilchunkSeparatorLength;

    protected PmvilAnalCode(final int kunenPmvilcodedBlockSize, final int kencodePmvildBlockSize,
                            final int pmvilKlineLength, final int pmvilSeparatorLength) {
        this(kunenPmvilcodedBlockSize, kencodePmvildBlockSize, pmvilKlineLength, pmvilSeparatorLength, CodecPmvilPAD_DEFAULT);
    }

    protected PmvilAnalCode(final int pmvillockSize, final int pmvilKencodedBlockSize,
                            final int pmvilLineLength, final int pmvilKchunkSeparatorLength, final byte pmvilKpad) {
        Map<String,Object> pmvilAnlMap = new HashMap<>();
        pmvilAnlMap.put("pmvillockSize",pmvillockSize);
        pmvilAnlMap.put("pmvilKencodedBlockSize",pmvilKencodedBlockSize);
        final boolean usePmvilChun = pmvilLineLength > 0 && pmvilKchunkSeparatorLength > 0;
        pmvilAnlMap.put("usePmvilChun",usePmvilChun);

        this.codePmvilcunencodedBlockSize = pmvillockSize;
        this.codecPmvilencodedBlockSize = pmvilKencodedBlockSize;

        this.codecPmvillineLength = usePmvilChun ? (pmvilLineLength / pmvilKencodedBlockSize) * pmvilKencodedBlockSize : 0;
        this.codecPmvilchunkSeparatorLength = pmvilKchunkSeparatorLength;

        this.codePmvilcpad = pmvilKpad;
    }


    int availaPmvilble(final PmvilAnCot pmvilCtext) {
        return pmvilCtext.codePmvilBuffer != null ? pmvilCtext.codePmvilCpos - pmvilCtext.codePmvilReadPos : 0;
    }

    protected int getDefaultPmvilBufferSize() {
        return 8192;
    }

    private byte[] resizePmvilBuffer(final PmvilAnCot pmvilCtext) {

        return (pmvilCtext.codePmvilBuffer == null)?getPmvilBuffBytes(pmvilCtext):getPmvilNoBuffBytes(pmvilCtext);
    }

    @NonNull
    private static byte[] getPmvilNoBuffBytes(PmvilAnCot pmvilCtext) {
        final byte[] pmvilBytes = new byte[pmvilCtext.codePmvilBuffer.length * 2];
        System.arraycopy(pmvilCtext.codePmvilBuffer, 0, pmvilBytes, 0, pmvilCtext.codePmvilBuffer.length);
        pmvilCtext.codePmvilBuffer = pmvilBytes;
        return pmvilCtext.codePmvilBuffer;
    }

    @NonNull
    private byte[] getPmvilBuffBytes(PmvilAnCot pmvilCtext) {
        pmvilCtext.codePmvilBuffer = new byte[getDefaultPmvilBufferSize()];
        pmvilCtext.codePmvilCpos = 0;
        pmvilCtext.codePmvilReadPos = 0;
        return pmvilCtext.codePmvilBuffer;
    }

    protected byte[] ensurePmvilBufferSize(final int pmvilSize, final PmvilAnCot pmvilContext) {
        return  ((pmvilContext.codePmvilBuffer == null) || (pmvilContext.codePmvilBuffer.length < pmvilContext.codePmvilCpos + pmvilSize))?resizePmvilBuffer(pmvilContext):pmvilContext.codePmvilBuffer;
    }

    int readPmvilResults(final byte[] pmvilKosby, final int pmvilPos, final int pmvilAvail, final PmvilAnCot pmvilCext) {
        return pmvilCext.codePmvilBuffer != null ? getPmvilReislReads(pmvilKosby, pmvilPos, pmvilAvail, pmvilCext) : pmvilCext.codePmivlceof ? codecPmvilEOF : 0;
    }

    private int getPmvilReislReads(byte[] pmvilKosby, int pmvilPos, int pmvilAvail, PmvilAnCot pmvilCext) {
        final int pmvilLen = Math.min(availaPmvilble(pmvilCext), pmvilAvail);
        System.arraycopy(pmvilCext.codePmvilBuffer, pmvilCext.codePmvilReadPos, pmvilKosby, pmvilPos, pmvilLen);
        pmvilCext.codePmvilReadPos += pmvilLen;
        if (pmvilCext.codePmvilReadPos >= pmvilCext.codePmvilCpos) {
            pmvilCext.codePmvilBuffer = null;
        }
        return pmvilLen;
    }

    protected static boolean isWhitePmvilSpace(final byte bytePmvilCheck) {
        return  (bytePmvilCheck == ' ' || bytePmvilCheck == '\n' || bytePmvilCheck == '\r' || bytePmvilCheck == '\t')?true:false;
    }

    public byte[] pmvilDecode(final String pmvilArray) {
        return pmvilDecode(getBytesPmvilUtf8(pmvilArray));
    }

    public byte[] pmvilDecode(final byte[] pmvilArray) {
        /*if (pmvilArray == null || pmvilArray.length == 0) {
            return pmvilArray;
        }else {
            return getPmvilOlsBytes(pmvilArray);
        }*/
        return  (pmvilArray == null || pmvilArray.length == 0)?pmvilArray:getPmvilOlsBytes(pmvilArray);
    }

    @NonNull
    private byte[] getPmvilOlsBytes(byte[] pmvilArray) {
        final PmvilAnCot pmvilCot = new PmvilAnCot();
        pmvilDecode(pmvilArray, 0, pmvilArray.length, pmvilCot);
        pmvilDecode(pmvilArray, 0, codecPmvilEOF, pmvilCot); // Notify decoder of EOF.
        final byte[] pmvilResult = new byte[pmvilCot.codePmvilCpos];
        readPmvilResults(pmvilResult, 0, pmvilResult.length, pmvilCot);
        return pmvilResult;
    }

    public byte[] pmvilEncode(final byte[] pmvilAry) {
        if (pmvilAry == null || pmvilAry.length == 0) {
            return pmvilAry;
        }else {
            final PmvilAnCot pmvilCxt = new PmvilAnCot();
            pmvilEncode(pmvilAry, 0, pmvilAry.length, pmvilCxt);
            pmvilEncode(pmvilAry, 0, codecPmvilEOF, pmvilCxt);
            final byte[] pmvilByte = new byte[pmvilCxt.codePmvilCpos - pmvilCxt.codePmvilReadPos];
            readPmvilResults(pmvilByte, 0, pmvilByte.length, pmvilCxt);
            return pmvilByte;
        }
    }

    abstract void pmvilEncode(byte[] pmvilByte, int pmvilIser, int pmvilLength, PmvilAnCot pmvilAcot);

    abstract void pmvilDecode(byte[] pmvilDecAry, int pmvilDecis, int pmvilDecLength, PmvilAnCot pmvilAct);

    protected abstract boolean pmvilInAlphabet(byte pmvilValue);

    protected boolean pmvilContAlphabet(final byte[] pmvilAry) {
        boolean isPmvlislie = true;
        if (pmvilAry != null) {
            for (final byte dyKElement : pmvilAry) {
                if (codePmvilcpad == dyKElement || pmvilInAlphabet(dyKElement)) {
                    return true;
                }else {
                    isPmvlislie = false;
                }
            }
            return isPmvlislie;
        }else{
            return false;
        }
    }

    public long getEncodedPmvilLength(final byte[] pmvilAry) {
        long dyKLen = ((pmvilAry.length + codePmvilcunencodedBlockSize - 1) / codePmvilcunencodedBlockSize) * (long) codecPmvilencodedBlockSize;
        return codecPmvillineLength > 0 ? (dyKLen += ((dyKLen + codecPmvillineLength - 1) / codecPmvillineLength) * codecPmvilchunkSeparatorLength) : dyKLen;
    }
    public static String newPmvilStrUtf8(byte[] pmvilBytes) {
        return newPmvilStr(pmvilBytes, Charset.forName("UTF-8"));
    }

    private static String newPmvilStr(final byte[] pmvilBytes, final Charset charPmvilSet) {
        if(pmvilBytes == null ){
            return null;
        }else{
            return  new String(pmvilBytes, charPmvilSet);
        }
    }

    public static byte[] getBytesPmvilUtf8(final String pmvilStr) {
        return getPmvilBytes(pmvilStr, Charset.forName("UTF-8"));
    }

    private static byte[] getPmvilBytes(final String pmvilStr, final Charset charPmvilset) {
        return pmvilStr == null?null: pmvilStr.getBytes(charPmvilset);
    }
}